/**

Copyright (C) SYSTAP, LLC DBA Blazegraph 2006-2016.  All rights reserved.

Contact:
     SYSTAP, LLC DBA Blazegraph
     2501 Calvert ST NW #106
     Washington, DC 20008
     licenses@blazegraph.com

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/
/*
 * Created on Jun 14, 2010
 */

package com.bigdata.quorum;

import java.util.UUID;

/**
 * A non-remote interface containing <em>only</em> and <em>all</em> distributed
 * quorum state change messages for this {@link QuorumMember}. These messages
 * are generated by the {@link QuorumWatcher} for the {@link QuorumMember} when
 * it notices the corresponding change in the distributed quorum state. Thus,
 * all methods on this interface indicate <em>observed</em> state changes. The
 * {@link QuorumActor} is responsible for causing changes in the distributed
 * quorum state, but it DOES NOT generate these messages - that task falls to
 * the {@link QuorumWatcher}.
 * <p>
 * Quorum members have strict preconditions on their actions, which are
 * documented by the {@link QuorumActor}. In addition, {@link QuorumWatcher}
 * maintains various postconditions (for example, a member leave implies a
 * service leave). Together, these preconditions and postconditions imply an
 * ordering over the {@link QuorumStateChangeListener}s.
 * <p>
 * However, because this interface reports <em>observed</em> state changes, it
 * is possible that some events may occur "out of order". For example, if the
 * quorum is maintained within zookeeper and a client looses its zookeeper
 * connection, then zookeeper will the <i>ephemeral</i> znodes for that client.
 * The <i>order</i> in which other clients observe those state changes in
 * essentially arbitrary.
 * <p>
 * Implementations of this interface must not block in the event thread.
 * 
 * @see QuorumMember
 * @see QuorumActor
 * @see QuorumWatcher
 * 
 * @author <a href="mailto:thompsonbry@users.sourceforge.net">Bryan Thompson</a>
 * @version $Id: QuorumStateChangeListener.java 4069 2011-01-09 20:58:02Z
 *          thompsonbry $
 * 
 * @see <a href="https://sourceforge.net/apps/trac/bigdata/ticket/695">
 *      HAJournalServer reports "follower" but is in SeekConsensus and is not
 *      participating in commits (javadoc clarifications)</a>
 */
public interface QuorumStateChangeListener {

    /**
     * Invoked when this service is added as a quorum member.
     */
    void memberAdd();

    /**
     * Invoked when this service is removed as a quorum member.
     */
    void memberRemove();

    /**
     * Invoked when this service is added to the write pipeline. The service
     * always enters at the of the pipeline.
     */
    void pipelineAdd();

    /**
     * Invoked when this service is removed from the write pipeline.
     */
    void pipelineRemove();

    /**
     * Invoked for this service when the service is already in the pipeline and
     * this service becomes the first service in the write pipeline because all
     * previous services in the pipeline order have been removed from the
     * pipeline (failover into the leader position).
     */
    void pipelineElectedLeader();
    
    /**
     * Invoked for this service when the downstream service in the write
     * pipeline has changed. Services always enter at the end of the write
     * pipeline, but may be removed at any position in the write pipeline.
     * 
     * @param oldDownstreamId
     *            The {@link UUID} of the service which <em>was</em> downstream
     *            from this service in the write pipeline and <code>null</code>
     *            iff this service was the last service in the pipeline.
     * @param newDownstreamId
     *            The {@link UUID} of the service which <em>is</em> downstream
     *            from this service in the write pipeline and <code>null</code>
     *            iff this service <em>is</em> the last service in the pipeline.
     */
    void pipelineChange(final UUID oldDownStreamId, final UUID newDownStreamId);
    
    /**
     * Invoked for this service when the upstream service in the write pipeline
     * has been removed. This hook provides an opportunity for this service to
     * close out its connection with the old upstream service and to prepare to
     * establish a new connection with the new downstream service.
     */
    void pipelineUpstreamChange();

//    void castVote(long lastCommitTime);
//    void withdrawVote(long lastCommitTime);
//    void setLastValidToken();
    
//    /**
//     * Invoked when <em>this</em> quorum member is elected as the quorum leader.
//     */
//    void electedLeader();
//
//    /**
//     * Invoked when <em>this</em> quorum member is elected as a quorum follower.
//     * This event occurs both when the quorum meets and when a quorum member is
//     * becomes synchronized with and then joins an already met quorum.
//     */
//    void electedFollower();

    /**
     * Invoked when a consensus has been achieved among <code>(k+1)/2</code>
     * services concerning a shared lastCommitTime (really, this is not a
     * consensus but a simple majority). This message is sent to each member
     * service regardless of whether or not they participated in the consensus.
     * <p>
     * Once a consensus has been reached, each {@link QuorumMember} which agrees
     * on that <i>lastCommitTime</i> MUST do a {@link #serviceJoin()} before the
     * quorum will meet. The first quorum member to do a service join will be
     * elected the leader. The remaining services to do a service join will be
     * elected followers.
     * 
     * @param lastCommitTime
     *            The last commit time around which a consensus was established.
     *
     * @see #serviceJoin()
     * @see #electedLeader(long)
     * @see #electedFollower(long)
     * @see #lostConsensus()
     */
    void consensus(final long lastCommitTime);

    /**
     * Invoked when the consensus is lost. Services do not withdraw their cast
     * votes until a quorum breaks and a new consensus needs to be established.
     * This message is sent to each member service regardless of whether or not
     * they participated in the consensus.
     * 
     * @see #consensus(long)
     */
    void lostConsensus();
    
    /**
     * Invoked when this service joins the quorum.
     */
    void serviceJoin();

    /**
     * Invoked when this service leaves the quorum.
     */
    void serviceLeave();

//    /**
//     * Invoked for all quorum members when the leader leaves the quorum. A
//     * leader leave is also a quorum break, so services can generally just
//     * monitor {@link #quorumBreak()} instead of this method. Also, services
//     * will generally notice a quorum break because {@link Quorum#token()} will
//     * have been cleared and will in any case not be the same token under which
//     * the service was operating.
//     */
//    void leaderLeft();

    /**
     * Invoked when a quorum meets. The state of the met quorum can be queried
     * using the <i>token</i>. Quorum members can use this to decide whether
     * they are the leader (using {@link #isLeader(long)}, joined as a
     * follower (using {@link #isFollower(long)}), or do not participate
     * in the quorum (this message is sent to all quorum members, so this
     * service might not be part of the met qourum).
     * <p>
     * The following pre-conditions will be satisfied before this message is
     * sent to the {@link QuorumMember}:
     * <ul>
     * <li>There will be at least <code>(k+1)/2</code> services which have voted
     * for the same <i>lastCommitTime</i>.</li>
     * <li>There will be at <code>(k+1)/2</code> services joined with the
     * quorum. The {@link Quorum#getJoined() join order} will be the same
     * as the {@link Quorum#getVotes()} for the services which voted for the
     * <i>lastCommitTime</li> around which a consensus was formed.</li>
     * <li>If this quorum member is joined with the quorum it will have observed
     * its own {@link #memberAdd()}, {@link #pipelineAdd()},
     * {@link #consensus(long)}, and {@link #serviceJoin()} events.</li>
     * <li>The joined services will be arranged in a write pipeline, with the
     * leader at the head of that pipeline.</li>
     * </ul>
     * <p>
     * When control returns from this method, the following post-conditions
     * should be true:
     * <ul>
     * <li>The service should be prepared to accept reads.</li>
     * <li>If the service was elected as the quorum leader, then it should be
     * prepared to accept writes.</li>
     * </ul>
     * If the {@link QuorumMember} is joined with the quorum but it can not
     * satisfy these post-conditions, then it must
     * {@link QuorumActor#serviceLeave() leave} the {@link Quorum}.
     * 
     * @param token
     *            The newly assigned quorum token.
     * @param leaderId
     *            The {@link UUID} of the service which was elected to be the
     *            quorum leader. This information is only valid for the scope of
     *            the accompanying quorum token. (The leaderId may be obtained
     *            from {@link #getLeader(long)} at any time for a met quorum.)
     */
    void quorumMeet(long token, UUID leaderId);

    /**
     * Invoked when a quorum breaks. The service MUST handle handle this event
     * by (a) doing an abort() which will any buffered writes and reload their
     * current root block; and (b) casting a vote for their current commit time.
     * Once a consensus is reached on the current commit time, services will be
     * joined in the vote order, a new leader will be elected, and the quorum
     * will meet again. This message is sent to all member services, regardless
     * of whether they were joined with the met quorum.
     */
    void quorumBreak();

}
